package com.ruoyi.business.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.business.domain.*;
import com.ruoyi.business.domain.bo.ExamBo;
import com.ruoyi.business.domain.bo.UserExamBo;
import com.ruoyi.business.domain.bo.UserExamQuestionBo;
import com.ruoyi.business.domain.vo.ExamVo;
import com.ruoyi.business.enumeration.UserExamEnum;
import com.ruoyi.business.mapper.*;
import com.ruoyi.business.question.*;
import com.ruoyi.business.service.*;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.domain.model.InternalObjectPage;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.*;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.system.domain.SysOss;
import com.ruoyi.system.mapper.SysOssMapper;
import com.ruoyi.system.service.ISysTableObjService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigInteger;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * @author 最爱喝兽奶
 * @create 2022/8/10
 **/
@SuppressWarnings("ALL")
@Service
public class ExamServiceImpl extends ServiceImpl<ExamMapper, Exam> implements IExamService {


    @Autowired
    private IExamQuestionService examQuestionService;

    @Autowired
    private THomeworkStuMapper homeworkStuMapper;

    @Autowired
    private IUserExamService userExamService;

    @Autowired
    private CourseInfoMapper courseInfoMapper;


    @Autowired
    private ISysTableObjService sysTableObjService;

    @Autowired
    private SysOssMapper ossMapper;

    @Autowired
    private IUserExamQuestionService userExamQuestionService;

    @Autowired
    private CourseUserMapper courseUserMapper;

    @Autowired
    private UserExamMapper userExamMapper;

    @Autowired
    private ExamQuestionMapper examQuestionMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private MailUtils mailUtils;

    private final Logger logger = LoggerFactory.getLogger(ExamServiceImpl.class);

    public static final Integer EXAM_NUM = 3;

    @Override
    @Transactional
    public void insertExam(ExamBo bo) {
        Exam exam = new Exam();
        BeanUtils.copyBeanProp(exam, bo, true);
        // 设置试卷总分
        Integer total = bo.getSingleNum() * bo.getSingleScore() + bo.getMultiplyNum() * bo.getMultiplyScore() + bo.getJudgeNum() * bo.getJudgeScore() + bo.getFillNum() * bo.getFillScore() + bo.getSubjectiveNum() * bo.getSubjectiveScore();
        exam.setScore(total);
        if (StringUtils.isBlank(bo.getPublisher())) {
            // 如果没有传入上传者的id，就在后台设置
            exam.setPublisher(String.valueOf(SecurityUtils.getUserId()));
        }
        // 判断当前考试是否已存在图片
        if (StringUtils.isBlank(exam.getExamImage())) {
            // 将课程的图片设为考试图片
            CourseInfo courseInfo = courseInfoMapper.selectById(exam.getCourseId());
            if (courseInfo == null) {
                throw new RuntimeException("传入了错误的课程id");
            }
            if (StringUtils.isBlank(courseInfo.getCourseImage())) {
                throw new RuntimeException("需要先上传对应的课程海报！");
            }
            exam.setExamImage(courseInfo.getCourseImage());
        }
        baseMapper.insert(exam);
        // 生成考试信息
        generateExam(exam, EXAM_NUM);
        // 生成考试信息之后绑定考试和学生之间的关系
        bindingExamUser(exam);
    }

    /**
     * 绑定考试和学生之间的关系
     * @param exam
     */
    private void bindingExamUser(Exam exam) {
        // 查询出当前课程的所有学生
        List<String> userIds = courseUserMapper.selectUserIdsByCourse(exam.getCourseId());
//        List<String> userIds = homeworkStuMapper.selectStuByCourse(exam.getCourseId());
        // 用户和考试的列表
        List<UserExam> userExams = new ArrayList<>();
        List<String> emails = new ArrayList<>();
        String username = SecurityUtils.getUsername();
        // 将学生分成三等分
        for (int i = 0; i < userIds.size(); i++) {
            // 给当前用户分配的考试编号
            int num = i % EXAM_NUM + 1;
            User user = userMapper.selectById(userIds.get(i));
            emails.add(user.getEmail());
            UserExam userExam = new UserExam();
            userExam.setExamId(exam.getId());
            userExam.setUserId(userIds.get(i));
            userExam.setNum(num);
            userExam.setStatus(UserExamEnum.IN_COMPLETE.getValue());
            userExams.add(userExam);
        }
        userExamService.saveBatch(userExams);
        logger.info("批量插入考生信息成功!!");
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        mailUtils.sendMessage(emails,
                String.format("%s老师, 发布了考试<%s>, 考试时间为%s到%s, 请注意考试时间.", username, exam.getTitle(), sdf.format(exam.getStartTime()), sdf.format(exam.getEndTime())),
                "考试");
    }

    @Override
    public TableDataInfo<ExamVo> selectExamByPage(ExamBo bo) {
        Page<Exam> page = baseMapper.selectPage(PageUtils.buildPage(bo), buildQueryWrapper(bo));
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        List<ExamVo> list = page.getRecords().stream().map(item -> {
            ExamVo vo = new ExamVo();
            BeanUtils.copyBeanProp(vo, item, true);
            if (StringUtils.isNotBlank(item.getExamImage())) {
                SysOss sysOss = ossMapper.selectById(item.getExamImage());
                vo.setExamImage(sysOss.getUrl());
            }
            vo.setStartTime(item.getStartTime());
            vo.setEndTime(item.getEndTime());
            return vo;
        }).collect(Collectors.toList());
        Page<ExamVo> target = PageUtils.convert(page, list);
        return PageUtils.buildTableDataInfo(target);
    }

    @Override
    public TableDataInfo<ExamVo> queryStuExamByPage(UserExamBo bo) {
        if (StringUtils.isBlank(bo.getUserId())) {
            // 如果没有传入用户id，就从后台获取当前的登录用户
            bo.setUserId(String.valueOf(SecurityUtils.getUserId()));
        }
        bo.setStatus(UserExamEnum.IN_COMPLETE.getValue());
        Page<ExamVo> page = baseMapper.queryStuExamByPage(PageUtils.buildPage(bo), bo);
        List<ExamVo> collect = page.getRecords().stream().map(item -> {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
            try {
                Date startTime = sdf.parse(item.getStartTime());
                Date endTime = sdf.parse(item.getEndTime());
                Long currentTime = System.currentTimeMillis();
                if (startTime.getTime() < currentTime
                        && endTime.getTime() > currentTime) {
                    item.setStatus(1);
                }else {
                    // TODO：写代码的时候为了方便，到后面要把这个1改成0
                    item.setStatus(0);
                }
                if (StringUtils.isNotBlank(item.getCourseId())) {
                    CourseInfo courseInfo = courseInfoMapper.selectById(item.getCourseId());
                    item.setCourseName(courseInfo.getCourseName());
                }
            } catch (ParseException e) {
                e.printStackTrace();
            }
            return item;
        }).filter(item -> item.getStatus() == 1).collect(Collectors.toList());
        page.setRecords(collect);
        return PageUtils.buildTableDataInfo(page);
    }

    @Override
    @Transactional
    public void submitExam(List<UserExamQuestionBo> bos) {
        // 将学生的答题情况存入到数据库中
        AtomicReference<Integer> totalScore = new AtomicReference<>(0);
        // 根据考试id和考试编号，将所有的考试题目信息查询出来
        List<ExamQuestion> examQuestions = examQuestionMapper.selectList(new QueryWrapper<ExamQuestion>().eq("exam_id", bos.get(0).getExamId()).eq("num", bos.get(0).getExamNum()));
        List<UserExamQuestion> collect = bos.stream().map(item -> {
            UserExamQuestion userExamQuestion = new UserExamQuestion();
            BeanUtils.copyBeanProp(userExamQuestion, item, true);
            userExamQuestion.setUserId(String.valueOf(SecurityUtils.getUserId()));
            // 根据题型获取问题处理器
           QuestionHandle questionHandle = QuestionHandleFactory.createQuestionHandle(item.getType());
            Integer score = new Integer(0);
            for (ExamQuestion examQuestion : examQuestions) {
                if (item.getExamId().equals(examQuestion.getExamId()) && item.getExamNum().equals(examQuestion.getNum())) {
                    score = questionHandle.calcScore(item.getAnswer(), item.getQuestionId(), examQuestion.getScore());
                }
            }
            if (null != score) {
                // 计算总分
                Integer finalScore = score;
                totalScore.updateAndGet(v -> v + finalScore);
            }
            userExamQuestion.setScore(score);
            return userExamQuestion;
        }).collect(Collectors.toList());
        // 批量将考试信息存入
        userExamQuestionService.saveBatch(collect);
        // 如果当前考试不存在主观题，就直接计算出分数
        UserExam userExam = new UserExam();
        userExam.setExamId(bos.get(0).getExamId());
        userExam.setNum(bos.get(0).getExamNum());
        userExam.setUserId(SecurityUtils.getUserId());
        // 设置客观题得分
        userExam.setObjectiveScore(totalScore.get());
        if (!hasSubjectiveQuestion(bos)) {
            // 判断是否存在主观题
            userExam.setScore(totalScore.get());
            // 如果试卷不存在主观题，直接主观题得分就是0了
            userExam.setSubjectiveScore(0);
        }else {
            // 存在主观题

        }
        // 设置成考试结束
        userExam.setStatus(UserExamEnum.COMPLETE.getValue());
        // 将考试信息存入数据库
        userExamMapper.updateScoreByOther(userExam);
    }

    private boolean hasSubjectiveQuestion(List<UserExamQuestionBo> bos) {
        for (UserExamQuestionBo bo : bos) {
            if ("3".equals(bo.getType()) || "4".equals(bo.getType())) {
                return true;
            }
        }
        return false;
    }

    @Override
    public InternalObjectPage selectRefTableByName(String refColumn, Integer pageSize, Integer currentPage, String filterContent, String filterId) {
        InternalObjectPage pageDomain = new InternalObjectPage();
        //主键Map
        Map<String, String[]> pkMap = new HashMap<>();
        Map<String, String> map = new HashMap<>();
        //标题头
        Map<String, Map<String, String>> titleMap = new LinkedHashMap();
        String sql = null;
        Page pageList = new Page();
        Page page = new Page();
        //是否开启分页，不开启默认查询前10000条记录（前端已限制默认分页数量，此处为保障内部对象的正常使用）
        if (currentPage == null || pageSize == null) {
            page = new Page(1, 10000);
        } else {
            page = new Page(currentPage, pageSize);
        }
        // 决定引用哪个字段，这个数组中包含的是字段名
        String[] refColumns = {"exam"};
        if (StringUtils.equalsAnyIgnoreCase(refColumn, refColumns)) {
            Map<String, String> classMap = new LinkedHashMap();
            classMap.put("showColumn", "考试");
            titleMap.put(refColumn, classMap);
            pkMap.put(refColumn, new String[]{"id", "title"});
            map.put(refColumn, "SELECT id as \"refColumn\",title as \"showColumn\"   FROM T_EXAM");

            sql = map.get(refColumn);
            //筛选
            if (StringUtils.isNotEmpty(filterContent)) {
                if (StringUtils.isNotEmpty(pkMap.get(refColumn)[1])) {
                    sql = sql + " where  " + pkMap.get(refColumn)[1] + " like " + "'%" + filterContent + "%'";
                }
            }
            //回显与判断
            if (StringUtils.isNotEmpty(filterId)) {
                List<String> list = Arrays.asList(filterId.split(","));
                String ids = list.stream().map(s -> "\'" + s + "\'").collect(Collectors.joining(","));
                if (StringUtils.isNotEmpty(pkMap.get(refColumn))) {
                    sql = sql + " where " + pkMap.get(refColumn)[0] + " in " + "(" + ids + ")";
                }
            }
        }
        try {
            pageList = sysTableObjService.executeSelectSqlSelectRefTableByName(page, sql);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException().setMessage("内部对象字段配置信息错误");
        }
        List<Map<String, String>> records = (List<Map<String, String>>) pageList.getRecords().stream().map(item -> {
            if (item instanceof Map) {
                Map m = (Map) item;
                if (m.containsKey("refColumn")) {
                    Object value = m.get("refColumn");
                    if (value instanceof BigInteger) {
                        m.put("refColumn", String.valueOf(value));
                    } else if (value instanceof Long) {
                        m.put("refColumn", String.valueOf(value));
                    }
                }
            }
            return item;
        }).collect(Collectors.toList());
        pageDomain.setTotal(page.getTotal());
        pageDomain.setTitle(titleMap.get(refColumn));
        pageDomain.setDataList(records);
        return pageDomain;
    }

    @Override
    public ExamVo queryExamVo(String id) {
        Exam exam = getById(id);
        ExamVo vo = new ExamVo();
        BeanUtils.copyProperties(exam, vo);
        return vo;
    }

    private LambdaQueryWrapper<Exam> buildQueryWrapper(ExamBo bo) {
        LambdaQueryWrapper<Exam> lqw = new LambdaQueryWrapper<>();
        lqw.like(StringUtils.isNotBlank(bo.getTitle()), Exam::getTitle, bo.getTitle());
        return lqw;
    }

    /**
     * 传入考试生成信息，随机生成三套试卷
     * @param exam 考试信息
     * @param num num套试卷
     */
    private void generateExam(Exam exam, int num) {
        // TODO： 逻辑初步写完，还没测试
        List<SysDictData> homeworkType = DictUtils.getDictCache("homework_type");
        List<ExamQuestion> examQuestionList = new ArrayList<>();
        for (int i = 1; i <= num; i++) {
            // 查询
            assert homeworkType != null;
            for (SysDictData sysDictData : homeworkType) {
                QuestionHandle questionHandle = QuestionHandleFactory.createQuestionHandle(sysDictData.getDictValue());
                List<String> questionIds = null;
                // 当前题型的分值
                Integer score = 0;
                if (questionHandle instanceof SingleChoiceQuestionHandle) {
                    // 如果当前是单选题
                    questionIds = questionHandle.randQueryQuestion(exam.getCourseId(), exam.getSingleNum());
                    score = exam.getSingleScore();
                } else if (questionHandle instanceof MultipleChoiceQuestionHandle) {
                    // 如果当前是多选题
                    questionIds = questionHandle.randQueryQuestion(exam.getCourseId(), exam.getMultiplyNum());
                    score = exam.getMultiplyScore();
                } else if (questionHandle instanceof JudgeQuestionHandle) {
                    // 如果当前是判断题
                    questionIds = questionHandle.randQueryQuestion(exam.getCourseId(), exam.getJudgeNum());
                    score = exam.getJudgeScore();
                } else if (questionHandle instanceof FillQuestionHandle) {
                    // 如果当前是填空题
                    questionIds = questionHandle.randQueryQuestion(exam.getCourseId(), exam.getFillNum());
                    score = exam.getFillScore();
                } else if (questionHandle instanceof SubjectiveQuestionHandle) {
                    // 如果当前是主观题
                    questionIds = questionHandle.randQueryQuestion(exam.getCourseId(), exam.getSubjectiveNum());
                    score = exam.getSubjectiveScore();
                }
                if (questionIds == null) continue;
                // 将映射关系，填入list中
                for (String questionId : questionIds) {
                    ExamQuestion examQuestion = new ExamQuestion();
                    examQuestion.setExamId(exam.getId());
                    // 设置试卷题号
                    examQuestion.setNum(i);
                    examQuestion.setQuestionId(questionId);
                    examQuestion.setScore(score);
                    examQuestionList.add(examQuestion);
                }
            }
            // 批量插入一套试卷
            examQuestionService.saveBatch(examQuestionList);
            examQuestionList.clear();
        }
        log.debug("批量插入题目成功");
    }
}
